package com.sky.skyopsserver.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sky.skyopsserver.domain.Credential;
import com.sky.skyopsserver.domain.Host;
import com.sky.skyopsserver.enums.CredentialTypeEnum;
import com.sky.skyopsserver.enums.ResponseCodeEnum;
import com.sky.skyopsserver.exception.BizException;
import com.sky.skyopsserver.mapper.CredentialMapper;
import com.sky.skyopsserver.mapper.HostMapper;
import com.sky.skyopsserver.service.CredentialService;
import com.sky.skyopsserver.vos.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 凭据信息服务实现类
 * 
 * @author Administrator
 * @description 针对表【credential(凭据信息表)】的数据库操作Service实现
 * @createDate 2025-09-13 17:43:04
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CredentialServiceImpl extends ServiceImpl<CredentialMapper, Credential>
        implements CredentialService {

    private final HostMapper hostMapper;
    
    // 加密密钥（实际项目中应该从配置文件读取）
    private static final String ENCRYPT_KEY = "SkyOpsServer2025";
    private static final String ALGORITHM = "AES";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long addCredential(CredentialAddReqVO reqVO) {
        log.info("开始添加凭据，凭据名称: {}, 用户名: {}", reqVO.getName(), reqVO.getAccount());
        
        // 验证凭据类型
        if (!validateCredentialType(reqVO.getType())) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_TYPE_INVALID);
        }
        
        // 验证凭据信息完整性
        if (!validateCredentialInfo(reqVO.getType(), reqVO.getPassword(), reqVO.getSecretKey())) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_TYPE_INVALID);
        }
        
        // 检查凭据名称是否已存在
        if (checkCredentialNameExists(reqVO.getName(), null)) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_NAME_EXISTS);
        }
        
        // 构建凭据实体
        Credential credential = Credential.builder()
                .name(reqVO.getName())
                .account(reqVO.getAccount())
                .type(reqVO.getType())
                .password(reqVO.getPassword())
                .secretKey(reqVO.getSecretKey())
                .remark(reqVO.getRemark())
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
        
        // 加密敏感信息
        credential = encryptCredential(credential);
        
        // 保存到数据库
        boolean saved = save(credential);
        if (!saved) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_ADD_FAILED);
        }
        
        log.info("凭据添加成功，凭据ID: {}", credential.getId());
        return credential.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateCredential(CredentialUpdateReqVO reqVO) {
        log.info("开始更新凭据，凭据ID: {}", reqVO.getId());
        
        // 检查凭据是否存在
        Credential existingCredential = getById(reqVO.getId());
        if (Objects.isNull(existingCredential)) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_NOT_EXISTS);
        }
        
        // 验证凭据类型
        if (!validateCredentialType(reqVO.getType())) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_TYPE_INVALID);
        }
        
        // 验证凭据信息完整性
        if (!validateCredentialInfo(reqVO.getType(), reqVO.getPassword(), reqVO.getSecretKey())) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_TYPE_INVALID);
        }
        
        // 检查凭据名称是否已被其他凭据使用
        if (checkCredentialNameExists(reqVO.getName(), reqVO.getId())) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_NAME_EXISTS);
        }
        
        // 更新凭据信息
        Credential credential = new Credential();
        BeanUtils.copyProperties(reqVO, credential);
        credential.setUpdateTime(LocalDateTime.now());
        
        // 加密敏感信息
        credential = encryptCredential(credential);
        
        boolean updated = updateById(credential);
        if (!updated) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_UPDATE_FAILED);
        }
        
        log.info("凭据更新成功，凭据ID: {}", reqVO.getId());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteCredential(Long id) {
        log.info("开始删除凭据，凭据ID: {}", id);
        
        // 检查凭据是否存在
        Credential existingCredential = getById(id);
        if (Objects.isNull(existingCredential)) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_NOT_EXISTS);
        }
        
        // 检查凭据下是否有主机
        Long hostCount = getHostCountByCredentialId(id);
        if (hostCount > 0) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_IN_USE);
        }
        
        boolean deleted = removeById(id);
        if (!deleted) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_DELETE_FAILED);
        }
        
        log.info("凭据删除成功，凭据ID: {}", id);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchDeleteCredential(List<Long> ids) {
        log.info("开始批量删除凭据，凭据ID列表: {}", ids);
        
        if (CollectionUtils.isEmpty(ids)) {
            throw new BizException(ResponseCodeEnum.PARAM_NOT_VALID);
        }
        
        // 检查所有凭据是否存在
        List<Credential> existingCredentials = listByIds(ids);
        if (existingCredentials.size() != ids.size()) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_NOT_EXISTS);
        }
        
        // 检查凭据下是否有主机
        Map<Long, Long> hostCountMap = getHostCountByCredentialIds(ids);
        List<Long> credentialsInUse = hostCountMap.entrySet().stream()
                .filter(entry -> entry.getValue() > 0)
                .map(Map.Entry::getKey)
                .toList();
        
        if (!credentialsInUse.isEmpty()) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_IN_USE);
        }
        
        boolean deleted = removeByIds(ids);
        if (!deleted) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_DELETE_FAILED);
        }
        
        log.info("凭据批量删除成功，删除数量: {}", ids.size());
        return true;
    }

    @Override
    public CredentialRspVO getCredentialById(Long id) {
        log.info("查询凭据详情，凭据ID: {}", id);
        
        Credential credential = getById(id);
        if (Objects.isNull(credential)) {
            throw new BizException(ResponseCodeEnum.CREDENTIAL_NOT_EXISTS);
        }
        
        // 解密敏感信息
        credential = decryptCredential(credential);
        
        CredentialRspVO rspVO = new CredentialRspVO();
        BeanUtils.copyProperties(credential, rspVO);
        
        // 设置认证方式描述
        CredentialTypeEnum typeEnum = CredentialTypeEnum.getByType(credential.getType());
        if (typeEnum != null) {
            rspVO.setTypeDescription(typeEnum.getDescription());
        }
        
        // 脱敏显示敏感信息
        rspVO.setPasswordMask(maskPassword(credential.getPassword()));
        rspVO.setSecretKeyMask(maskSecretKey(credential.getSecretKey()));
        
        // 设置主机数量
        Long hostCount = getHostCountByCredentialId(id);
        rspVO.setHostCount(hostCount);
        
        return rspVO;
    }

    @Override
    public IPage<CredentialRspVO> getCredentialPage(CredentialPageReqVO reqVO) {
        log.info("分页查询凭据列表，查询条件: {}", reqVO);
        
        // 构建分页对象
        Page<Credential> page = new Page<>(reqVO.getCurrent(), reqVO.getSize());
        
        // 构建查询条件
        LambdaQueryWrapper<Credential> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.hasText(reqVO.getName()), Credential::getName, reqVO.getName())
                   .like(StringUtils.hasText(reqVO.getAccount()), Credential::getAccount, reqVO.getAccount())
                   .eq(Objects.nonNull(reqVO.getType()), Credential::getType, reqVO.getType())
                   .like(StringUtils.hasText(reqVO.getRemark()), Credential::getRemark, reqVO.getRemark())
                   .orderByDesc(Credential::getUpdateTime);
        
        // 执行分页查询
        IPage<Credential> credentialPage = page(page, queryWrapper);
        
        // 获取所有凭据的主机数量
        List<Long> credentialIds = credentialPage.getRecords().stream()
                .map(Credential::getId)
                .collect(Collectors.toList());
        Map<Long, Long> hostCountMap = getHostCountByCredentialIds(credentialIds);
        
        // 转换为响应VO
        IPage<CredentialRspVO> rspPage = credentialPage.convert(credential -> {
            // 解密敏感信息
            credential = decryptCredential(credential);
            
            CredentialRspVO rspVO = new CredentialRspVO();
            BeanUtils.copyProperties(credential, rspVO);
            
            // 设置认证方式描述
            CredentialTypeEnum typeEnum = CredentialTypeEnum.getByType(credential.getType());
            if (typeEnum != null) {
                rspVO.setTypeDescription(typeEnum.getDescription());
            }
            
            // 脱敏显示敏感信息
            rspVO.setPasswordMask(maskPassword(credential.getPassword()));
            rspVO.setSecretKeyMask(maskSecretKey(credential.getSecretKey()));
            
            rspVO.setHostCount(hostCountMap.getOrDefault(credential.getId(), 0L));
            return rspVO;
        });
        
        log.info("分页查询凭据列表完成，总记录数: {}", rspPage.getTotal());
        return rspPage;
    }

    @Override
    public List<CredentialOptionRspVO> getAllCredentialOptions() {
        log.info("查询所有凭据选项");
        
        List<Credential> credentials = list(new LambdaQueryWrapper<Credential>()
                .orderByAsc(Credential::getName));
        
        // 获取所有凭据的主机数量
        List<Long> credentialIds = credentials.stream()
                .map(Credential::getId)
                .collect(Collectors.toList());
        Map<Long, Long> hostCountMap = getHostCountByCredentialIds(credentialIds);
        
        return credentials.stream()
                .map(credential -> convertToOptionVO(credential, hostCountMap.getOrDefault(credential.getId(), 0L)))
                .collect(Collectors.toList());
    }

    @Override
    public List<CredentialOptionRspVO> getCredentialOptionsByType(Integer type) {
        log.info("根据认证方式查询凭据选项，认证方式: {}", type);
        
        List<Credential> credentials = list(new LambdaQueryWrapper<Credential>()
                .eq(Credential::getType, type)
                .orderByAsc(Credential::getName));
        
        // 获取主机数量
        List<Long> credentialIds = credentials.stream()
                .map(Credential::getId)
                .collect(Collectors.toList());
        Map<Long, Long> hostCountMap = getHostCountByCredentialIds(credentialIds);
        
        return credentials.stream()
                .map(credential -> convertToOptionVO(credential, hostCountMap.getOrDefault(credential.getId(), 0L)))
                .collect(Collectors.toList());
    }

    @Override
    public List<CredentialOptionRspVO> getCredentialOptionsWithHosts() {
        log.info("查询有主机使用的凭据选项");
        
        // 先获取有主机的凭据ID（需要根据实际业务调整）
        List<Long> credentialIdsWithHosts = hostMapper.selectList(new LambdaQueryWrapper<Host>()
                        .select(Host::getCredential) // 假设Host表中有credential字段
                        .isNotNull(Host::getCredential)
                        .groupBy(Host::getCredential))
                .stream()
                .map(host -> Long.valueOf(host.getCredential())) // 需要根据实际字段类型调整
                .collect(Collectors.toList());
        
        if (CollectionUtils.isEmpty(credentialIdsWithHosts)) {
            return new ArrayList<>();
        }
        
        List<Credential> credentials = listByIds(credentialIdsWithHosts);
        Map<Long, Long> hostCountMap = getHostCountByCredentialIds(credentialIdsWithHosts);
        
        return credentials.stream()
                .map(credential -> convertToOptionVO(credential, hostCountMap.getOrDefault(credential.getId(), 0L)))
                .sorted(Comparator.comparing(CredentialOptionRspVO::getName))
                .collect(Collectors.toList());
    }

    @Override
    public List<CredentialOptionRspVO> getUnusedCredentialOptions() {
        log.info("查询未使用的凭据选项");
        
        // 获取所有凭据
        List<Credential> allCredentials = list();
        
        // 获取有主机的凭据ID（需要根据实际业务调整）
        Set<Long> credentialIdsWithHosts = new HashSet<>(); // 这里需要根据实际业务逻辑实现
        
        // 过滤出未使用的凭据
        return allCredentials.stream()
                .filter(credential -> !credentialIdsWithHosts.contains(credential.getId()))
                .map(credential -> convertToOptionVO(credential, 0L))
                .sorted(Comparator.comparing(CredentialOptionRspVO::getName))
                .collect(Collectors.toList());
    }

    @Override
    public CredentialStatisticsRspVO getCredentialStatistics() {
        log.info("获取凭据统计信息");
        
        // 凭据总数
        long totalCount = count();
        
        // 各类型凭据数量
        long passwordTypeCount = count(new LambdaQueryWrapper<Credential>()
                .eq(Credential::getType, CredentialTypeEnum.PASSWORD.getType()));
        long secretKeyTypeCount = count(new LambdaQueryWrapper<Credential>()
                .eq(Credential::getType, CredentialTypeEnum.SECRET_KEY.getType()));
        long passwordAndKeyTypeCount = count(new LambdaQueryWrapper<Credential>()
                .eq(Credential::getType, CredentialTypeEnum.PASSWORD_AND_KEY.getType()));
        
        // 主机总数（需要根据实际业务调整）
        long totalHostCount = hostMapper.selectCount(new LambdaQueryWrapper<>());
        
        // 有主机使用的凭据数量（需要根据实际业务调整）
        long credentialsWithHostsCount = 0; // 这里需要根据实际业务逻辑实现
        
        // 未使用的凭据数量
        long unusedCredentialsCount = totalCount - credentialsWithHostsCount;
        
        // 本月新增凭据数量
        YearMonth currentMonth = YearMonth.now();
        LocalDateTime monthStart = currentMonth.atDay(1).atStartOfDay();
        LocalDateTime monthEnd = currentMonth.atEndOfMonth().atTime(23, 59, 59);
        
        long monthlyAddedCount = count(new LambdaQueryWrapper<Credential>()
                .between(Credential::getCreateTime, monthStart, monthEnd));
        
        // 平均每凭据主机数
        double avgHostsPerCredential = totalCount > 0 ? (double) totalHostCount / totalCount : 0.0;
        
        return CredentialStatisticsRspVO.builder()
                .totalCount(totalCount)
                .passwordTypeCount(passwordTypeCount)
                .secretKeyTypeCount(secretKeyTypeCount)
                .passwordAndKeyTypeCount(passwordAndKeyTypeCount)
                .credentialsWithHostsCount(credentialsWithHostsCount)
                .unusedCredentialsCount(unusedCredentialsCount)
                .monthlyAddedCount(monthlyAddedCount)
                .totalHostCount(totalHostCount)
                .avgHostsPerCredential(Math.round(avgHostsPerCredential * 100.0) / 100.0)
                .build();
    }

    @Override
    public Boolean checkCredentialNameExists(String name, Long excludeId) {
        LambdaQueryWrapper<Credential> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Credential::getName, name);
        
        if (Objects.nonNull(excludeId)) {
            queryWrapper.ne(Credential::getId, excludeId);
        }
        
        return count(queryWrapper) > 0;
    }

    @Override
    public List<CredentialRspVO> searchCredentialByName(String name) {
        log.info("根据名称模糊查询凭据，关键字: {}", name);
        
        List<Credential> credentials = list(new LambdaQueryWrapper<Credential>()
                .like(StringUtils.hasText(name), Credential::getName, name)
                .orderByAsc(Credential::getName));
        
        return convertToRspVOList(credentials);
    }

    @Override
    public List<CredentialRspVO> searchCredentialByAccount(String account) {
        log.info("根据用户名模糊查询凭据，关键字: {}", account);
        
        List<Credential> credentials = list(new LambdaQueryWrapper<Credential>()
                .like(StringUtils.hasText(account), Credential::getAccount, account)
                .orderByAsc(Credential::getName));
        
        return convertToRspVOList(credentials);
    }

    @Override
    public Long getHostCountByCredentialId(Long credentialId) {
        // 这里需要根据实际业务逻辑实现，假设Host表中有credential字段;
        // return hostMapper.selectCount(new LambdaQueryWrapper<Host>();
        //         .eq(Host::getCredential, credentialId.toString()));
        return 0L; // 临时返回0，需要根据实际业务调整
    }

    @Override
    public Map<Long, Long> getHostCountByCredentialIds(List<Long> credentialIds) {
        if (CollectionUtils.isEmpty(credentialIds)) {
            return new HashMap<>();
        }
        
        // 这里需要根据实际业务逻辑实现
        // 临时返回空Map，需要根据实际业务调整
        Map<Long, Long> hostCountMap = new HashMap<>();
        credentialIds.forEach(id -> hostCountMap.put(id, 0L));
        return hostCountMap;
    }

    @Override
    public Boolean validateCredentialType(Integer type) {
        return CredentialTypeEnum.isValid(type);
    }

    @Override
    public Boolean validateCredentialInfo(Integer type, String password, String secretKey) {
        CredentialTypeEnum typeEnum = CredentialTypeEnum.getByType(type);
        if (typeEnum == null) {
            return false;
        }

        return switch (typeEnum) {
            case PASSWORD -> StringUtils.hasText(password);
            case SECRET_KEY -> StringUtils.hasText(secretKey);
            case PASSWORD_AND_KEY -> StringUtils.hasText(password) && StringUtils.hasText(secretKey);
        };
    }

    @Override
    public Credential encryptCredential(Credential credential) {
        try {
            if (StringUtils.hasText(credential.getPassword())) {
                credential.setPassword(encrypt(credential.getPassword()));
            }
            if (StringUtils.hasText(credential.getSecretKey())) {
                credential.setSecretKey(encrypt(credential.getSecretKey()));
            }
            return credential;
        } catch (Exception e) {
            log.error("凭据加密失败", e);
            throw new BizException(ResponseCodeEnum.CREDENTIAL_ENCRYPT_FAILED);
        }
    }

    @Override
    public Credential decryptCredential(Credential credential) {
        try {
            if (StringUtils.hasText(credential.getPassword())) {
                credential.setPassword(decrypt(credential.getPassword()));
            }
            if (StringUtils.hasText(credential.getSecretKey())) {
                credential.setSecretKey(decrypt(credential.getSecretKey()));
            }
            return credential;
        } catch (Exception e) {
            log.error("凭据解密失败", e);
            throw new BizException(ResponseCodeEnum.CREDENTIAL_DECRYPT_FAILED);
        }
    }

    @Override
    public String maskPassword(String password) {
        if (!StringUtils.hasText(password)) {
            return "";
        }
        if (password.length() <= 2) {
            return "*".repeat(password.length());
        }
        return password.charAt(0) + "*".repeat(password.length() - 2) + password.charAt(password.length() - 1);
    }

    @Override
    public String maskSecretKey(String secretKey) {
        if (!StringUtils.hasText(secretKey)) {
            return "";
        }
        if (secretKey.length() <= 20) {
            return "*".repeat(Math.min(secretKey.length(), 10));
        }
        return secretKey.substring(0, 10) + "..." + "*".repeat(10);
    }

    @Override
    public Boolean testCredentialConnection(Long credentialId, String host, Integer port) {
        log.info("测试凭据连接，凭据ID: {}, 主机: {}, 端口: {}", credentialId, host, port);
        
        // 这里需要根据实际业务实现SSH连接测试
        // 临时返回true，实际项目中需要实现真正的连接测试
        return true;
    }

    @Override
    public List<Map<String, Object>> getAllCredentialTypes() {
        log.info("获取所有认证方式列表");
        
        List<Map<String, Object>> types = new ArrayList<>();
        for (CredentialTypeEnum typeEnum : CredentialTypeEnum.values()) {
            Map<String, Object> typeMap = new HashMap<>();
            typeMap.put("type", typeEnum.getType());
            typeMap.put("description", typeEnum.getDescription());
            types.add(typeMap);
        }
        return types;
    }

    /**
     * 转换为选项VO
     * 
     * @param credential 凭据实体
     * @param hostCount 主机数量
     * @return 选项VO
     */
    private CredentialOptionRspVO convertToOptionVO(Credential credential, Long hostCount) {
        CredentialTypeEnum typeEnum = CredentialTypeEnum.getByType(credential.getType());
        return CredentialOptionRspVO.builder()
                .id(credential.getId())
                .name(credential.getName())
                .account(credential.getAccount())
                .type(credential.getType())
                .typeDescription(typeEnum != null ? typeEnum.getDescription() : "")
                .hostCount(hostCount)
                .remark(credential.getRemark())
                .build();
    }

    /**
     * 转换为响应VO列表
     * 
     * @param credentials 凭据列表
     * @return 响应VO列表
     */
    private List<CredentialRspVO> convertToRspVOList(List<Credential> credentials) {
        // 获取主机数量
        List<Long> credentialIds = credentials.stream()
                .map(Credential::getId)
                .collect(Collectors.toList());
        Map<Long, Long> hostCountMap = getHostCountByCredentialIds(credentialIds);
        
        return credentials.stream()
                .map(credential -> {
                    // 解密敏感信息
                    credential = decryptCredential(credential);
                    
                    CredentialRspVO rspVO = new CredentialRspVO();
                    BeanUtils.copyProperties(credential, rspVO);
                    
                    // 设置认证方式描述
                    CredentialTypeEnum typeEnum = CredentialTypeEnum.getByType(credential.getType());
                    if (typeEnum != null) {
                        rspVO.setTypeDescription(typeEnum.getDescription());
                    }
                    
                    // 脱敏显示敏感信息
                    rspVO.setPasswordMask(maskPassword(credential.getPassword()));
                    rspVO.setSecretKeyMask(maskSecretKey(credential.getSecretKey()));
                    
                    rspVO.setHostCount(hostCountMap.getOrDefault(credential.getId(), 0L));
                    return rspVO;
                })
                .collect(Collectors.toList());
    }

    /**
     * 加密字符串
     * 
     * @param plainText 明文
     * @return 密文
     */
    private String encrypt(String plainText) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(ENCRYPT_KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encrypted);
    }

    /**
     * 解密字符串
     * 
     * @param cipherText 密文
     * @return 明文
     */
    private String decrypt(String cipherText) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(ENCRYPT_KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(cipherText));
        return new String(decrypted, StandardCharsets.UTF_8);
    }
}




